#ifndef __MULTISCALEIMAGE_H__
#define __MULTISCALEIMAGE_H__

#include "cairo/cairo.h"
#include "glib/glib-2.0/glib.h"
#include <deque>

#include "multiscalesubimage.h"
#include "Context.h"
#include "DeepZoomImageDLL.h"
#include "surface-cairo.h"

using namespace std;

namespace DeepZoomImage
{
	struct BitmapImageContext;	
	struct Size;
	struct Rect;
	struct QTree;
	class MultiScaleTileSource;
	class Region;
	class MultiScaleSubImageCollection;
	class BitmapImage;

	typedef enum _PropertyChanged
	{
		PropertyChangedNone = 0,
		AnimatedViewportOriginProperty,
		AnimatedViewportWidthProperty,		
		ViewportWidthProperty,
		ViewportOriginProperty,
		AspectRatioProperty,
		BlurFactorProperty,
		TileFadeProperty,
		SourceProperty,
		UseSpringsProperty,
		ZIndexProperty,
		PropertyChangedTotal,
	}
	PropertyChanged;

	typedef enum _ChangedEvent
	{
		ChangedEventNone,
		ViewportChangedEvent,		
		ImageOpenSucceededEvent,
		ImageOpenFailedEvent,
		ImageFailedEvent,
		MotionFinishedEvent,
		InvalidatedEvent,
		ChangeEventTotal,
	}
	ChangedEvent;

	typedef enum _CollectionChangedEvent
	{
		CollectionChangedEventNone,
		CollectionChangedActionReplace,
		CollectionChangedActionAdd,
		CollectionChangedActionRemove,
		CollectionChangedActionClearing,
		CollectionChangedActionCleared,
		CollectionChangedEventTotal,
	}
	CollectionChangedEvent;

	typedef enum _ElementFlags
	{
		NONE = 0x00,

		// these two flags correspond to the 2 states of VisibilityProperty
		RENDER_VISIBLE   = 0x02,
		// the HitTestVisible property
		HIT_TEST_VISIBLE = 0x04,

		TOTAL_RENDER_VISIBLE   = 0x08,
		TOTAL_HIT_TEST_VISIBLE = 0x10,
	}
	ElementFlags;

	typedef enum _loadTileThreadMsg {
		MSG_LT_NONE = WM_USER,
		MSG_LT_BEGIN,
		MSG_LT_LOAD,
		MSG_LT_QUIT,
	}
	LoadTileThreadMsg;

	const int max_buffer_tile_num = 2;

	typedef struct _tagTile
	{
		UINT _layerIndex;
		UINT _row;
		UINT _col;
		ULONG _timeStamp;
		QTree  *pNode;

		_tagTile()
		{
			reset();
		}
		void reset()
		{
			_layerIndex = 0;
			_row = _col = 0;
			_timeStamp = 0;
			pNode = NULL;
		}
	}
	TagTile, LoadedTile;

	#define MAX_CACHE_TILE_QUEUE_SIZE (1000)   //2000

	//multiply the cores of CPU
	#define MAX_DOWNLOADERS 32

	class DEEP_ZOOM_IMAGE_DLL MultiScaleImage
	{
	public:
		MultiScaleImage ();
		virtual ~MultiScaleImage ();

		void UpdateStatus();
		void UpdateIdleStatus();
		void UpdateLoading();

		void Render();
		void OnPropertyChanged( PropertyChanged _propertyChangedID, void *args );
		void OnCollectionChanged( CollectionChangedEvent CCEventID, MultiScaleSubImage *subimage, int count);
		void OnCollectionItemChanged( PropertyChanged _propertyChangedID );
		Size MeasureOverrideWithError (Size availableSize);
		Size ComputeActualSize ();

		bool CanFindElement () { return GetSource () != NULL; }

		void UriSourceChanged ();
		void HandleDzParsed ();

		//
		// Methods
		//		
		//@zoomIncrementFactor: Specifies the zoom. This number is greater than 0. 
		//    A value of 1 specifies that the image fit the allotted page size exactly. A number greater than 1 specifies to zoom in. 
		//	   If a value of 0 or less is used, failure is returned and no zoom changes are applied. 
		//@zoomCenterLogicalX : X coordinate for the point on the MultiScaleImage that is zoomed in on. This is a logical point (between 0 and 1). 
		//@zoomCenterLogicalY: Y coordinate for the point on the MultiScaleImage that is zoomed in on. This is a logical point (between 0 and 1).
		void ZoomAboutLogicalPoint (double zoomIncrementFactor, double zoomCenterLogicalX, double zoomCenterLogicalY);
		Point ElementToLogicalPoint (Point elementPoint);		
		Point LogicalToElementPoint (Point logicalPoint);

		MultiScaleSubImage *GetIthSubImage (int index);		
		int LogicalToElementX (int x, int y);		
		int LogicalToElementY (int x, int y);		
		int GetSubImageCount ();

		//
		// Callback Methods
		//
		void TileOpened (BitmapImageContext *ctx);
		void TileFailed (BitmapImageContext *ctx);
		void EmitImageOpenSucceeded ();
		void EmitImageOpenFailed ();
		void EmitMotionFinished ();
		void EmitImageFailed ();
		void MotionFinished ();
		void FadeFinished ();
		void ZoomFinished ();
		void PanFinished ();

		static void tile_layer_invalidated ( gpointer closure, int level, int tilePosX, int tilePosY, int tileLayer );
		static void uri_source_changed (gpointer closure);

		void LoadCompleted();
		void LoadFailed();
		static void fade_finished(gpointer closure);
		static void zoom_finished (gpointer closure);
		static void pan_finished(gpointer closure);
		static void tile_opened (gpointer closure);
		static void tile_failed (gpointer closure);
		static void void_handler (gpointer closure);
		List * GetLoaders() { return &m_loaders; }

		void DynamicLoading();
		bool pushLoadedTileToCache( int layerIdx, int _row, int _col, QTree *pNode );
		void removeLoadedTileFromCache( int layerIdx, int _row, int _col, QTree *pNode );
		void removeLoadedTileFromCache( int _idx );
		void updateCacheQueueTimeStamp( int layerIdx, int _row, int _col, int _val, bool updateAll );
		LoadedTile * findTileInCacheQueue( int layerIdx, int _row, int _col );
		int getTileIndexInCacheQueue( int layerIdx, int _row, int _col );
		bool unloadTileByLRU();

		//
		// Property Accessors
		//
		inline double GetAspectRatio () { return m_AspectRatio; }	

		inline double GetBlurFactor() { return m_BlurFactor; }
		inline void SetBlurFactor (double value) { m_BlurFactor = value; }

		bool GetIsIdle ();

		bool GetIsLoading ();

		MultiScaleTileSource *GetSource () { return m_pMSTSource; }
		void SetSource (MultiScaleTileSource *source) { m_pMSTSource = source; }

		inline bool GetUseSprings () { return m_UseSprings; }
		inline void SetUseSprings (bool spring) { m_UseSprings = spring; }

		inline Point *GetViewportOrigin () { return &m_ViewportOrigin; }
		inline void SetViewportOrigin (Point *p) { m_ViewportOrigin.x = p->x; m_ViewportOrigin.y = p->y; }

		inline double GetViewportWidth() { return m_ViewportWidth; }
		inline void SetViewportWidth (double width) { m_ViewportWidth = width; }

		inline double GetActualWidth() { return m_ActualWidth; }
		inline double GetActualHeight() { return m_ActualHeight; }
		inline void   SetActualWidth(double actualWidth) {m_ActualWidth = actualWidth; }
		inline void   SetActualHeight(double actualHeight) {m_ActualHeight = actualHeight;}
		inline MultiScaleSubImageCollection *GetSubImages () { return m_pSubImages; }

		void LoadTile (void *tile, void *user_data);
		void unLoading ();
		bool CanLoadMoreTiles();

		void InvalidateTileLayer (int level, int tilePositionX, int tilePositionY, int tileLayer);

		void ProcessTile (BitmapImageContext *ctx);
		void RenderSingle (Context *ctx, Region *region);
		void RenderCollection (Context *ctx, Region *region);

		void SetIsIdle (bool value);

		void AnimateViewportOrigin (Point *origin);
		void SetAnimatedViewportOrigin(Point *origin);
		Point GetAnimatedViewportOrigin();

		void AnimateViewportWidth (double width);
		void SetAnimatedViewportWidth (double width);
		double GetAnimatedViewportWidth();

		inline double GetTileFade () { return m_TileFade; }

		void SetZoomAnimationEndPoint (double endpoint);
		double GetZoomAnimationEndPoint ();

		void SetPanAnimationEndPoint (Point endpoint);
		Point *GetPanAnimationEndPoint ();

		void SetCairoSurface(int width, int height,unsigned char * data);
		void SetCairoData(unsigned char* data);

#ifdef USE_ANIMATION
		Storyboard * CreateStoryboard();
		DoubleAnimation* CreateDoubleAnimation();
#endif

		void FullInvalidate (bool rendertransform);
		void Invalidate ();
		void Invalidate(Rect r);
		void InvalidateMeasure ();
		void UpdateBounds(bool force_redraw);
		void UpdateTransform();
		void UpdateProjection();
		bool Emit (int event_id);
		Size ApplySizeConstraints (const Size &size);

		Rect GetBounds () { return surface_bounds; }  // returns the current bounding box for the given item in surface coordinates.
		bool GetRenderVisible () { return (flags & TOTAL_RENDER_VISIBLE) != 0; }  //   Returns true if the Visibility property of this item is "Visible", and false otherwise
		Rect GetSubtreeBounds () { return surface_bounds; }  //   returns the bounding box including all sub-uielements. implemented by containers in surface coordinates.
		bool RenderToIntermediate ();
		void InvalidateBitmapCache ();

	protected:
		int m_InitLayers;
		int m_MaxLayers;
		int m_MaxCacheTiles;

		deque<LoadedTile *> *m_pCacheTileQueue;

	private:

#ifdef USE_ANIMATION
		DOPtr<DoubleAnimationUsingKeyFrames> zoom_animation;
		DOPtr<PointAnimationUsingKeyFrames> pan_animation;
		DOPtr<DoubleAnimation> fadein_animation;
		DOPtr<Storyboard> fadein_sb;
		DOPtr<Storyboard> zoom_sb;
		DOPtr<Storyboard> pan_sb;
#endif

		cairo_user_data_key_t full_opacity_at_key;
		bool subimages_sorted;
		List m_loaders;
		GHashTable *cache;
		double zoom_target;
		Point pan_target;
		int motion;
		Rect surface_bounds;
		double total_opacity;
		int flags;
		Region *dirty_region;

		double m_TileFade;
		Point m_AnimatedViewportOrigin;
		double m_AnimatedViewportWidth;

		double m_AspectRatio;    //the aspect ratio of the image used as the source of the MultiScaleImage. The aspect ratio is the width of the image divided by its height.
		double m_BlurFactor;      //the extent that data is blurred while rendering.
		bool m_IsIdle;                  //s a value that indicates whether Deep Zoom is done downloading, decoding, blending, and animating (if springs are being used) images. 
		MultiScaleTileSource *m_pMSTSource;      //the MultiScaleTileSource object that is used as the source for the MultiScaleImage
		MultiScaleSubImageCollection *m_pSubImages;   //the collection of MultiScaleSubImage objects within the multi-resolution image that is used by the MultiScaleImage.
		bool m_UseSprings;           // sets a value that indicates whether the MultiScaleImage uses spring animations.
		Point m_ViewportOrigin;    // the top-left corner of the area of the image to be displayed.
		double m_ViewportWidth;  //the width of the area of the image displayed.
		
		double m_Width;           //the width of the element.
		double m_Height;           //the suggested height of the element.
		double m_ActualWidth;   //the rendered width of this element.
		double m_ActualHeight;  //the rendered height of this element.
		double m_MaxWidth;     //the maximum width constraint of the element.
		double m_MaxHeight;    //the maximum height constraint of the element.
		double m_MinWidth;      //the minimum width constraint of the element.
		double m_MinHeight;	  //the minimum height constraint of the element.
		double m_OriginalPixelWidth;   //the original width of the image used as the source of the MultiScaleImage.
		double m_OriginalPixelHeight; //the original height of the image used as the source of the MultiScaleImage.
 
		Context *m_pContext;
		Region *m_pRegion;
		CairoSurface * m_pTarget;
	};
};
#endif /* __MULTISCALIMAGE_H__ */
